using System;
using System.Drawing;
using BmgNet;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;

namespace ObjectLib
{
    [Serializable]
    public class Circle: ICloneable, IXmlSerializable, ISerializable
    {
        public PointF Center;
        public float Radius;
        public bool Display;
        public string Name="";
        public Circle() { }
        public Circle(PointF center,float radius,bool display)
        {
            Center = center;
            Radius = radius;
            Display = display;
        }
        public Circle(Point center,int radius,bool display)
        {
            Center = new PointF((float)center.X,(float)center.Y);
            Radius = (float)radius;
            Display = display;
        }
        public bool SameCircleAs(Circle other)
        {
            return this.Center==other.Center && this.Radius==other.Radius;
        }
        public object Clone()
        {
            return new Circle(this.Center,this.Radius,this.Display);
        }
        public RectangleF BoundingRectF { get { return new RectangleF(Center.X-Radius,Center.Y-Radius,2.0f*Radius,2.0f*Radius); } }
        public override string ToString()
        {
            return "Radius=" + Convert.ToString((int)(Radius+0.5f)) + "; Center=("
				    + Convert.ToString((int)(Center.X+0.5f)) + ", "
				    + Convert.ToString((int)(Center.Y+0.5f)) + ")";
        }
        /// <summary>
        /// Converts this instance to a matlab structure of the given variable name.  The variable 'temp' in the matlab workspace is cleared.
        /// </summary>
        /// <param name="variableName">Contains everything appended before the fieldnames of the structure.  For example, "struct_name(1)."</param>
        /// <param name="matlab"></param>
        public void ToMatlabStruct(string varName,EngMATLib.EngMATAccess mat)
        {
            //	Center
            mat.EvaluateAsString(varName + "Center=["+this.Center.X.ToString()+", "+this.Center.Y.ToString()+"];");
            //	Radius
            mat.EvaluateAsString(varName + "Radius="+this.Radius.ToString()+";");
        }
        [NonSerialized]
        private static bool[,] calcMem;
        public void CalcStats(Ushort3 corePix,float rgbFac,bool raisedCosineWindow,out double[] mean,out double[,] cov,out int nValid,out int nInvalid)
        {
            if(calcMem==null || calcMem.GetLength(0) < 2*(int)Radius+1)
                calcMem = new bool[2*(int)Radius+1,2*(int)Radius+1];
            nValid = 0;
            nInvalid = 0;
            mean = new double[3];
            cov = new double[3,3];
            float[] rgb,lms,lyr;
            double weight=0.0,win=0.0,freq2pi=Math.PI/this.Radius,offsetSq;
            rgb = new float[3];
            lms = new float[3];
            lyr = new float[3];
            int left = (int)(Center.X-Radius);
            int right = (int)(Center.X+Radius+1.0f);
            int top = (int)(Center.Y-Radius);
            int bottom = (int)(Center.Y+Radius+1.0f);
            int rad = (int)this.Radius;
            int radsq = rad*rad;
            int xc = (int)(this.Center.X+0.5f);
            int yc = (int)(this.Center.Y+0.5f);
            int i,j,ii,jj;
            for(i=-rad;i<=rad;i++)
            {
                ii = i+xc;
                for(j=-rad;j<=rad;j++)
                {
                    jj = j+yc;
                    offsetSq = i*i+j*j;
                    if(offsetSq <= radsq)
                    {
                        if(jj<0 || jj>=corePix.Ni || ii<0 || ii>=corePix.Nj) //   Ni for jj and Nj for ii, reversed.
                        {
                            nInvalid++;
                            calcMem[j+rad,i+rad]=false;
                        }
                        else
                        {
                            rgb[0] = corePix[jj,ii,0];
                            rgb[1] = corePix[jj,ii,1];
                            rgb[2] = corePix[jj,ii,2];
                            if(rgb[0]>=4095.0f || rgb[1]>=4095.0f || rgb[2]>=4095.0f)
                            {
                                nInvalid++;
                                calcMem[j+rad,i+rad]=false;
                            }
                            else
                            {
                                nValid++;
                                calcMem[j+rad,i+rad]=true;
                                rgb[0]*=rgbFac;
                                rgb[1]*=rgbFac;
                                rgb[2]*=rgbFac;
                                KodakColorSpace.RgbToLogLms(rgb,lms);
                                KodakColorSpace.LogLmsToLumYbRg(lms,lyr);
                                if(raisedCosineWindow)
                                {
                                    win = 1.0 + Math.Cos(Math.Sqrt(offsetSq)*freq2pi);
                                    mean[0] += lyr[0];
                                    mean[1] += lyr[1];
                                    mean[2] += lyr[2];
                                    weight += win;
                                }
                                else
                                {
                                    mean[0] += lyr[0];
                                    mean[1] += lyr[1];
                                    mean[2] += lyr[2];
                                }
                            }
                        }
                    }
                    else
                    {
                        calcMem[j+rad,i+rad]=false;
                    }
                }
            }
            if((!raisedCosineWindow && nValid>=3) || (raisedCosineWindow && weight>=3.0))
            {
                if(raisedCosineWindow)
                {
                    mean[0] /= weight;
                    mean[1] /= weight;
                    mean[2] /= weight;
                }
                else
                {
                    mean[0] /= (double)nValid;
                    mean[1] /= (double)nValid;
                    mean[2] /= (double)nValid;
                }
                for(i=-rad;i<=rad;i++)
                {
                    ii = i+xc;
                    for(j=-rad;j<=rad;j++)
                    {
                        jj = j+yc;
                        if(calcMem[j+rad,i+rad])
                        {
                            rgb[0] = corePix[jj,ii,0];
                            rgb[1] = corePix[jj,ii,1];
                            rgb[2] = corePix[jj,ii,2];
                            rgb[0]*=rgbFac;
                            rgb[1]*=rgbFac;
                            rgb[2]*=rgbFac;
                            KodakColorSpace.RgbToLogLms(rgb,lms);
                            KodakColorSpace.LogLmsToLumYbRg(lms,lyr);
                            lyr[0]-=(float)mean[0];
                            lyr[1]-=(float)mean[1];
                            lyr[2]-=(float)mean[2];

                            if(raisedCosineWindow)
                            {
                                offsetSq = i*i+j*j;
                                win = 1.0 + Math.Cos(Math.Sqrt(offsetSq)*freq2pi);
                                cov[0,0] += (double)lyr[0]*(double)lyr[0]*win;
                                cov[1,1] += (double)lyr[1]*(double)lyr[1]*win;
                                cov[2,2] += (double)lyr[2]*(double)lyr[2]*win;

                                cov[0,1] += (double)lyr[0]*(double)lyr[1]*win;
                                cov[0,2] += (double)lyr[0]*(double)lyr[2]*win;

                                cov[1,2] += (double)lyr[1]*(double)lyr[2]*win;
                            }
                            else
                            {
                                cov[0,0] += (double)lyr[0]*(double)lyr[0];
                                cov[1,1] += (double)lyr[1]*(double)lyr[1];
                                cov[2,2] += (double)lyr[2]*(double)lyr[2];

                                cov[0,1] += (double)lyr[0]*(double)lyr[1];
                                cov[0,2] += (double)lyr[0]*(double)lyr[2];

                                cov[1,2] += (double)lyr[1]*(double)lyr[2];
                            }
                        }
                    }
                }
                if(raisedCosineWindow)
                {
                    cov[0,0] /= (weight-1.0);
                    cov[1,1] /= (weight-1.0);
                    cov[2,2] /= (weight-1.0);

                    cov[0,1] /= (weight-1.0);
                    cov[0,2] /= (weight-1.0);

                    cov[1,2] /= (weight-1.0);
                }
                else
                {
                    cov[0,0] /= (double)(nValid-1);
                    cov[1,1] /= (double)(nValid-1);
                    cov[2,2] /= (double)(nValid-1);

                    cov[0,1] /= (double)(nValid-1);
                    cov[0,2] /= (double)(nValid-1);

                    cov[1,2] /= (double)(nValid-1);
                }

                cov[1,0] = cov[0,1];
                cov[2,0] = cov[0,2];
                cov[2,1] = cov[1,2];
            }
        }

        #region IXmlSerializable Members

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            this.Center = new PointF(0.0f,0.0f);
            int depth = reader.Depth;
            bool reading=false;
            while((reading || reader.Read()) && reader.Depth>depth)
            {
                reading = false;
                if((reader.NodeType == System.Xml.XmlNodeType.Element || reader.NodeType==System.Xml.XmlNodeType.Text) && reader.Name!="")
                {
                    reading = true;
                    switch(reader.Name)
                    {
                        case "Radius":
                            {
                                this.Radius = reader.ReadElementContentAsFloat();
                            } break;
                        case "X":
                            {
                                this.Center.X = reader.ReadElementContentAsFloat();
                            } break;
                        case "Y":
                            {
                                this.Center.Y = reader.ReadElementContentAsFloat();
                            } break;
                        case "Display":
                            {
                                this.Display = Boolean.Parse(reader.ReadElementContentAsString());
                            } break;
                        case "Name":
                            {
                                this.Name = reader.ReadElementContentAsString();
                            } break;
                        default: throw new Exception("Unexpected field encounterd while reading XML for a PolygonF object.  Field name = " + reader.Name);
                    }
                }
            }
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteStartElement("Circle");
            writer.WriteElementString("Radius", Radius.ToString());
            writer.WriteElementString("X",Center.X.ToString());
            writer.WriteElementString("Y",Center.Y.ToString());
            writer.WriteElementString("Display",Display.ToString());
            writer.WriteElementString("Name",Name);
            writer.WriteEndElement();
        }

        #endregion
        #region ISerializable Members
        public Circle(SerializationInfo info,StreamingContext context)
        {
            Center = (PointF)info.GetValue("Center",typeof(PointF));
            Radius = info.GetSingle("Radius");
            Display = info.GetBoolean("Display");
            try{    Name = info.GetString("Name");}
            catch{  Name = "";}
        }
        public void GetObjectData(SerializationInfo info,StreamingContext context)
        {
            info.AddValue("Center", Center);
            info.AddValue("Radius",Radius);
            info.AddValue("Display",Display);
            info.AddValue("Name",Name);
        }

        #endregion
    }
}